#ifdef WIN32
#include <tinyxml/tinyxml.h>
#else
#include "tinyxml.h"
#endif
#include <algorithm>
#include <boost/foreach.hpp>
#include "Td_object.h"
#include "game_logic.h"
#include "animation.h"
#include <list>
//#undef check
//#include <boost/lambda/lambda.hpp>

extern cpVect g_gravity;
extern float_t g_explosive_range;
extern float_t g_explosive_amplitude;
extern std::vector<TotemObject_t*> g_TotemObjects;

namespace totem_destroyer{	
	std::list<boost::shared_ptr<ExplosionAnimation> > g_explosideAnimations;
	
	void SpaceMgr::initialize()
	{
		//todo, relase;
		cpInitChipmunk();
		ASSERT(NULL == m_space);
		// Optional. Read the docs to see what this really does.
		cpResetShapeIdCounter();
		// Create a space and adjust some of it's parameters.
		m_space = cpSpaceNew();
		cpSpaceResizeStaticHash(m_space, 400.0f, 40);
		cpSpaceResizeActiveHash(m_space, 100, 600);		

		//set gravity
		m_space->gravity = g_gravity;
		//m_space->damping = 0.78;

		glEnable(GL_LINE_SMOOTH);
		glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST);
		glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
		
		g_explosideAnimations.clear();
	}
	
	void ArbiterEachForBurnning(cpArray *arr, cpShape *shape)
	{
		BOOST_FOREACH(TotemObject_t* obj, g_TotemObjects){
			for(int i=0; i<arr->num; i++) {
				cpArbiter *arb = (cpArbiter *)arr->arr[i];
				if ( (obj->m_pShape == arb->private_a || obj->m_pShape == arb->private_b) 
					&& (shape == arb->private_a || shape == arb->private_b) ) {
					obj->setObjStatus(kTotemObjBurn);
					break;
				}
			}
		}
	}
	
	bool SpaceMgr::remove_shape(cpShape *shape)
	{
		bool ret = false;
		//m_resFree.erase(shape);
		action_t *p = (action_t *)shape->data;
		if(boost::tuples::get<0>(p->is_explosive())) {
			
			if(boost::tuples::get<1>(p->is_explosive())) {
				cpSpace * space = m_space;
				cpArray *bodies = space->bodies;
				cpBody *explosive  = shape->body;
				for(int i=0; i<bodies->num; i++){
					cpBody *body = (cpBody *)bodies->arr[i];
					cpVect p0p1 = cpvadd(body->p, cpvneg(explosive->p));
					cpFloat length = cpvlength(p0p1);
					if (length <= g_explosive_range) {						
						body->v = cpvadd(body->v, cpvmult(p0p1, (1.0 - length/g_explosive_range) * g_explosive_amplitude/length));
					}					
				}
			}
			//todo, music stuff
			//playExplode();
			game_logic::remainSteps()--;
			ret = true;
			
			const totem_obj_info_t *obj_info = (const totem_obj_info_t *)p->_getObjInfo();
			cpBody *body  = shape->body;			
			{
				AnimationBase::vert_t verts;
				const char * const digital = "0123456789";
				int x = 1;
				int y = 1;
				std::string::size_type pos = obj_info->texture_name.find_first_of(digital);				
				if (std::string::npos != pos) {
					x = atoi(obj_info->texture_name.c_str() + pos);
					pos = obj_info->texture_name.find_first_of(digital, pos+1);
					if (std::string::npos != pos) y = atoi(obj_info->texture_name.c_str() + pos);
				}								
				for(int i=0; i<4; i++){
					//cpVect v = cpvadd(body->p, cpvmult( cpvrotate(obj_info->verts[i], body->rot), 2.0f) );
					cpVect v = cpvadd(body->p, cpvrotate(obj_info->verts[i], body->rot));
					verts[i] = (float_t)v.x, (float_t)v.y;
				}
				int num;
				if (y % x) num = 1;
				else num = y/x;
				
				point2d_t p1p2 = verts[2] - verts[1];
				p1p2 /= num;
				
				for(int i = 0; i < num; i++) {
					boost::shared_ptr<ExplosionAnimation> explosion;
					explosion.reset(getAnimationFile<ExplosionAnimation>(obj_info));
					AnimationBase::vert_t &_verts = explosion->getVerts();
					verts[2] = verts[1] + p1p2;
					verts[3] = verts[0] + p1p2;
					_verts[0] = verts[0];
					_verts[1] = verts[1];
					_verts[2] = verts[2];
					_verts[3] = verts[3];
					{
						point2d_t center = (_verts[0] + _verts[1] + _verts[2] + _verts[3])/4;
						_verts[0] = _verts[0] * 2 - center;
						_verts[1] = _verts[1] * 2 - center;
						_verts[2] = _verts[2] * 2 - center;
						_verts[3] = _verts[3] * 2 - center;
					}
					verts[0] = verts[3];
					verts[1] = verts[2];					
					g_explosideAnimations.push_back(explosion);
				}
			}
			
			ArbiterEachForBurnning(m_space->arbiters, shape);
			p->_remove_from_space(m_space);
			p->_free();
			m_resFree.erase(shape);
		}
		return ret;
	}


	void SpaceMgr::release()
	{
		//using namespace boost::lambda;
		BOOST_FOREACH(const shapes_map_t::value_type & tmp, m_resFree){
			action_t *p = (action_t *)tmp->data;
			p->_remove_from_space(m_space);
			p->_free();
		}
		if (m_space) {
			cpSpaceFreeChildren(m_space);
			cpSpaceFree(m_space);
			m_space = NULL;
		}

		m_resFree.clear();
	}

	void draw_object_wrap(void *ptr, void *unused)
	{
		cpShape *shape = (cpShape *)ptr;
		if (NULL == shape->data) {
			ASSERT(0); return ;
		}
		action_t *p = (action_t *)shape->data;
		p->render_action();
	}
	
	void ArbiterEach(cpArray *arr)
	{
		BOOST_FOREACH(TotemObject_t* obj, g_TotemObjects){
			obj->isCollision() = false;
			for(int i=0; i<arr->num; i++) {
				cpArbiter *arb = (cpArbiter *)arr->arr[i];
				if (obj->m_pShape == arb->private_a || obj->m_pShape == arb->private_b) {
					obj->isCollision() = true;
					break;
				}
			}
			if (false == obj->isCollision()) {
				obj->setObjStatus(kTotemObjFallen);
				if (obj->m_pBoby->rot.x > 0.5) {
					obj->setObjStatus(kTotemObjFallen);
				}
			}

		}
	}

	void SpaceMgr::display()
	{
		ASSERT(m_space);

#ifdef WIN32
		glClear(GL_COLOR_BUFFER_BIT);
#endif
		ArbiterEach(m_space->arbiters);
		BOOST_FOREACH(const shapes_map_t::value_type & tmp, m_resFree){
			action_t *p = (action_t *)tmp->data;
			p->render_action();
		}
//		cpSpaceHashEach(m_space->activeShapes, &draw_object_wrap, NULL);
//		cpSpaceHashEach(m_space->staticShapes, &draw_object_wrap, NULL);
		
		for (std::list<boost::shared_ptr<ExplosionAnimation> >::iterator iter = g_explosideAnimations.begin(); 
			 iter != g_explosideAnimations.end();) {
			(*iter)->draw();
			if ((*iter)->runningOut()) {
				iter = g_explosideAnimations.erase(iter);
			}
			else {
				iter++;
			}
		}
		
	}

	void SpaceMgr::update( cpFloat dt )
	{
		ASSERT(m_space);
		//set or reset forces here if needed
		cpSpaceStep(m_space, dt);
	}
};